import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_demo/ex/object.dart';
import 'package:flutter_demo/pages/container/wrap_page.dart';
import 'package:flutter_demo/pages/page_with_title.dart';
import 'package:flutter_demo/widgets/app_widgets.dart';

import '../../conf/m3.dart';

class FlowPage extends PageWithTitle {
  const FlowPage() : super("流式布局(Flow)");

  @override
  List<Widget> buildContentWidgets(BuildContext context) {
    return const [
      PrimaryTitle("简介"),
      BodyText("这个跟Android的FlowLayout有些区别，实际上他是通过自定义绘制子视图来实现修改"
          "子视图显示位置，因为是基于绘制实现，所以性能较高，但若item需要点击事件，那就不太行了。"),
      LabelText("- 自定义绘制可以按任意规则去设置变换，所以可以实现各种排布以及流畅的动画效果"),
      LabelText("- 若需要点击事件，建议通过Wrap来实现，通过Stack+Positioned(Child)来实现"),
      NoticeText("因为Flow是基于变换的，子项无法在显示位置响应点击事件，一般要通过IgnorePointer组件使其忽略点击"),
      NoticeText("这个组件用着很麻烦，若仅需要流式布局建议直接使用Wrap组件实现"),
      PageLink(WrapPage()),
      AppSpacer.large,
      _Example(),
      UlItem("Flow"),
      LabelText("- delegate，代理对象，负责子组件的自定义绘制实现"),
      AppSpacer.large,
      OlItem(1, "FlowDelegate"),
      LabelText("- getSize，获取Flow的尺寸，默认是根据约束条件取最大值填充父容器"),
      LabelText("- getConstraintsForChild，获取子组件约束，默认使用Flow的约束条件，所以"
          "会跟Flow同尺寸，若需要自适应item大小可以覆写此方法返回新的约束"),
      LabelText("- shouldRelayout，这在重建Flow时创建了新的代理对象，需要通过这个方法判断"
          "更新代理对象是否需要重新布局"),
      LabelText("- shouldRepaint，根据旧的代理对象判断是否需要重新绘制"),
      LabelText("- paintChildren，绘制子组件"),
      LabelText("  - context.size flow尺寸"),
      LabelText("  - context.childCount 获取子组件数量"),
      LabelText("  - context.getChildSize 获取子组件尺寸"),
      LabelText("  - context.paintChild 根据变换绘制子组件"),
    ];
  }
}

class _Example extends StatelessWidget {
  const _Example();

  @override
  Widget build(BuildContext context) {
    final children = List.generate(30, _buildItem);
    return FlowLayout(
      children: children,
    );
  }

  Widget _buildItem(int index) {
    return Container(
      margin: const EdgeInsetsDirectional.all(M3.paddingM / 2),
      padding: EdgeInsetsDirectional.symmetric(
          horizontal: index * 1.0, vertical: M3.paddingS),
      color: Colors.blue,
      child: Text("$index"),
    );
  }
}

class FlowLayout extends StatefulWidget {
  final List<Widget> children;

  const FlowLayout({super.key, required this.children});

  @override
  State<StatefulWidget> createState() {
    return FlowLayoutState();
  }
}

class FlowLayoutState extends State<FlowLayout> {
  final _measurementKey = GlobalKey();
  List<Widget>? widgetChildren;

  var _hasMeasured = false;
  var _contentHeight = 0.0;

  void measureSize() async {
    if (!_hasMeasured) {
      //监听下一帧刷新，获取尺寸信息
      WidgetsBinding.instance.addPostFrameCallback((timestamp) {
        var renderStack =
            _measurementKey.currentContext?.findRenderObject() as RenderStack;
        //计算尺寸信息
        final containerWidth = renderStack.size.width;
        var offsetX = 0.0;
        var offsetY = 0.0;
        var lineHeight = 0.0;
        renderStack.visitChildren((child) {
          if (child is RenderBox) {
            if (!(offsetX == 0 && offsetY == 0) &&
                offsetX + child.size.width > containerWidth) {
              offsetX = 0;
              offsetY += lineHeight;
              lineHeight = 0;
            }
            offsetX += child.size.width;
            lineHeight = max(lineHeight, child.size.height);
          }
        });
        _contentHeight = offsetY + lineHeight;
        setState(() {
          _hasMeasured = true;
        });
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    if (widgetChildren != widget.children) {
      widgetChildren = widget.children;
      _hasMeasured = false;
      measureSize();
    }
    if (!_hasMeasured) {
      //临时插入到Widget树种进行测量，Offstage使得组件在组件树中不可见但可以测量尺寸
      return Offstage(
        child: Stack(
          key: _measurementKey,
          children: widget.children,
        ),
      );
    } else {
      return SizedBox(
        height: _contentHeight,
        child: Flow(
          delegate: _FlowDelegate(),
          children: widget.children,
        ),
      );
    }
  }
}

class _FlowDelegate extends FlowDelegate {
  @override
  Size getSize(BoxConstraints constraints) => constraints.biggest;

  @override
  BoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) {
    return BoxConstraints.loose(Size.infinite);
  }

  @override
  bool shouldRelayout(covariant FlowDelegate oldDelegate) {
    return false;
  }

  @override
  bool shouldRepaint(covariant FlowDelegate oldDelegate) {
    return false;
  }

  var containerWidth = 0.0;
  var containerHeight = 0.0;
  Size? childSize;

  var offsetX = 0.0;
  var offsetY = 0.0;
  var lineHeight = 0.0;

  @override
  void paintChildren(FlowPaintingContext context) {
    //模拟一下流式布局
    offsetX = 0.0;
    offsetY = 0.0;
    containerWidth = context.size.width;
    containerHeight = context.size.height;
    for (int i = 0; i < context.childCount; i++) {
      context.getChildSize(i)?.also((it) {
        if (!(offsetX == 0 && offsetY == 0) &&
            offsetX + it.width > containerWidth) {
          offsetX = 0;
          offsetY += lineHeight;
          lineHeight = 0;
        }
        context.paintChild(i,
            transform: Transform.translate(offset: Offset(offsetX, offsetY))
                .transform);
        offsetX += it.width;
        lineHeight = max(lineHeight, it.height);
      });
    }
  }
}
